/*
 * Copyright (c) 2013-2017 ARM Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ----------------------------------------------------------------------
 *
 * $Date:        1. December 2017
 * $Revision:    V2.0.0
 *
 * Project:      CMSIS-DAP Source
 * Title:        SW_DP.c CMSIS-DAP SW DP I/O
 *
 *---------------------------------------------------------------------------*/

#include "DAP_config.h"
#include "DAP.h"
#include "port_common.h"
#include "driver/dedic_gpio.h"
#include "driver/gpio.h"
#include "esp_check.h"
#include "dap_dedic_io_port.h"
// SW Macros
#define ERR_CHECK_OR_GOTO(ret, label) \
    do {                              \
        if ((ret) != ESP_OK)          \
            goto label;               \
    } while (0)
#define DEDIC_PIN_SWCLK_SET DEDIC_PIN_SWCLK_TCK_SET
#define DEDIC_PIN_SWCLK_CLR DEDIC_PIN_SWCLK_TCK_CLR

#define DEDIC_SW_CLOCK_CYCLE() \
    DEDIC_PIN_SWCLK_CLR();     \
    PIN_DELAY();               \
    DEDIC_PIN_SWCLK_SET();     \
    PIN_DELAY()

#define DEDIC_SW_WRITE_BIT(bit) \
    DEDIC_PIN_SWDIO_OUT(bit);   \
    DEDIC_PIN_SWCLK_CLR();      \
    PIN_DELAY();                \
    DEDIC_PIN_SWCLK_SET();      \
    PIN_DELAY()

#define DEDIC_SW_READ_BIT(bit)  \
    DEDIC_PIN_SWCLK_CLR();      \
    PIN_DELAY();                \
    bit = DEDIC_PIN_SWDIO_IN(); \
    DEDIC_PIN_SWCLK_SET();      \
    PIN_DELAY()

#define PIN_DELAY() PIN_DELAY_SLOW(DAP_Data.clock_delay)

#if (DAP_SWD != 0)
// struct dedic_io_bus_impl_t {
//     uint32_t SWCLK_bit;
//     uint32_t SWDIO_bit;
//     dedic_gpio_bundle_handle_t out_bundle;
//     dedic_gpio_bundle_handle_t bidir_bundle;
// };
const char *__attribute__((used)) DEDIC_IO_TAG = "dedic_io";
//static struct dedic_io_bus_impl_t *bus_impl = NULL;
//void IRAM_ATTR Dedic_IO_PORT_SWD_SETUP_ASM(uint32_t SWCLK_bit, uint32_t SWDIO_bit);
//void IRAM_ATTR Dedic_IO_PORT_SWD_SETUP_ASM(void);
void Dedic_IO_PORT_SWD_SETUP(void)
{
    esp_init_swd_pins();
    //     esp_err_t ret;

    //     /* 配置双向IO引脚(SWDIO/TMS) */
    //     int bidir_array[] = { DEDIC_PIN_SWDIO_TMS };
    //     dedic_gpio_bundle_config_t bidir_config = {
    //         .gpio_array = bidir_array, // 引脚数组
    //         .array_size = 1,           // 引脚数量
    //         .flags = {
    //             .out_en = 1, // 使能输出
    //             .in_en = 1   // 使能输入(双向模式)
    //         }
    //     };

    //     /* 配置输出引脚(SWCLK/TCK) */
    //     int output_array[] = { DEDIC_PIN_SWCLK_TCK };
    //     dedic_gpio_bundle_config_t output_config = {
    //         .gpio_array = output_array, // 引脚数组
    //         .array_size = 1,            // 引脚数量
    //         .flags = {
    //             .out_en = 1 // 仅使能输出
    //         }
    //     };
    //     /* 分配总线结构体内存 */
    //     if (bus_impl== NULL)
    //         bus_impl = malloc(sizeof(struct dedic_io_bus_impl_t));
    //     ESP_GOTO_ON_FALSE(bus_impl != NULL, ESP_ERR_NO_MEM, error, DEDIC_IO_TAG, "No more memory available in the system");

    //     /* 初始化专用GPIO bundle */
    //     ret = dedic_gpio_new_bundle(&output_config, &bus_impl->out_bundle);
    //     ERR_CHECK_OR_GOTO(ret, error_output);
    //     ret = dedic_gpio_new_bundle(&bidir_config, &bus_impl->bidir_bundle);
    //     ERR_CHECK_OR_GOTO(ret, error_bidir);

    //                               /**
    //      * 获取专用GPIO寄存器中的引脚位偏移：
    //      * - clk_bit: 时钟线在寄存器中的位偏移
    //      * - miso_bit: 输入数据线位偏移
    //      * - mosi/cs_bit: 基于时钟线偏移自动计算
    //      */
    //     ret = dedic_gpio_get_out_offset(bus_impl->out_bundle, &bus_impl->SWCLK_bit);
    //     ERR_CHECK_OR_GOTO(ret, error_offset);
    //     ret = dedic_gpio_get_in_offset(bus_impl->bidir_bundle, &bus_impl->SWDIO_bit);
    //     ERR_CHECK_OR_GOTO(ret, error_offset);
    //     ESP_LOGI(DEDIC_IO_TAG, "SWCLK_bit:%d, SWDIO_bit:%d", bus_impl->SWCLK_bit, bus_impl->SWDIO_bit);
    //     Dedic_IO_PORT_SWD_SETUP_ASM();
    //     return ;
    //     //Dedic_IO_PORT_SWD_SETUP_ASM(bus_impl->SWCLK_bit, bus_impl->SWDIO_bit);
    //     // gpio_write(DEDIC_PIN_SWDIO_TMS, 1U);
    //     // gpio_write(DEDIC_PIN_SWCLK_TCK, 1U);
    //     // gpio_set_mode(DEDIC_PIN_SWDIO_TMS, GPIO_OUTPUT_MODE);
    //     // gpio_set_mode(DEDIC_PIN_SWCLK_TCK, GPIO_OUTPUT_MODE);

    // error_offset :
    //     dedic_gpio_del_bundle(bus_impl->bidir_bundle);
    // error_bidir:
    //     dedic_gpio_del_bundle(bus_impl->out_bundle);
    // error_output:
    //     if (bus_impl != NULL) {
    //         free(bus_impl);
    //     }
    // error:
    //     return ;
}
#endif

// esp_err_t Dedic_IO_del(void)
// {
//     esp_err_t ret;
//     ESP_GOTO_ON_FALSE(bus_impl != NULL, ESP_ERR_INVALID_ARG, error, DEDIC_IO_TAG, "bus_impl must not be NULL");

//     dedic_gpio_del_bundle(bus_impl->out_bundle);
//     dedic_gpio_del_bundle(bus_impl->bidir_bundle);

//     free(bus_impl);
// error:
//     return ret;
// }

#if ((DAP_SWD != 0) || (DAP_JTAG != 0))
// Generate SWJ Sequence
//   count:  sequence bit count
//   data:   pointer to sequence bit data
//   return: none
//void IRAM_ATTR Dedic_IO_SWJ_Sequence_ASM(uint32_t count, const uint8_t *data, uint32_t clock_delay);
#include "esp_log.h"
void Dedic_IO_SWJ_Sequence(uint32_t count, const uint8_t *data)
{
    uint32_t val;
    uint32_t n;
    //ESP_LOGI("mdlink", "SWD Sequence: Dedic_MODE");

    val = 0U;
    n = 0U;
    while (count--) {
        if (n == 0U) {
            val = *data++;
            n = 8U;
        }
        if (val & 1U) {
            DEDIC_PIN_SWDIO_TMS_SET();
        } else {
            DEDIC_PIN_SWDIO_TMS_CLR();
        }
        DEDIC_SW_CLOCK_CYCLE();
        val >>= 1;
        n--;
    }
    //Dedic_IO_SWJ_Sequence_ASM(count, data, DAP_Data.clock_delay);
    // Dedic_IO_SWJ_Sequence_ASM(count, data, 30);
    // ESP_LOGI(DEDIC_IO_TAG, "ssssssssss:%d, sssss:%d", 1, 2);
    // uint32_t val;
    // uint32_t n;
    // val = 0U;
    // n = 0U;
    // while (count--) {
    //     if (n == 0U) {
    //         val = *data++;
    //         n = 8U;
    //     }
    //     if (val & 1U) {
    //         DEDIC_PIN_SWDIO_TMS_SET();
    //     } else {
    //         DEDIC_PIN_SWDIO_TMS_CLR();
    //     }
    //     DEDIC_SW_CLOCK_CYCLE();
    //     val >>= 1;
    //     n--;
    // }
}
#endif /* ((DAP_SWD != 0) || (DAP_JTAG != 0))*/

// Generate SWD Sequence
//   info:   sequence information
//   swdo:   pointer to SWDIO generated data
//   swdi:   pointer to SWDIO captured data
//   return: none
#if (DAP_SWD != 0)
/**
     * @brief 通过GPIO模拟方式发送SWJ序列
     * @param count 要发送的位数
     * @param data 要发送的数据指针
     * @note 使用GPIO逐位发送数据，每发送一位产生一个时钟周期
     */
void Dedic_IO_SWD_Sequence(uint32_t info, const uint8_t *swdo, uint8_t *swdi)
{
    uint32_t val;
    uint32_t bit;
    uint32_t n, k;

    n = info & SWD_SEQUENCE_CLK;
    if (n == 0U) {
        n = 64U;
    }

    if (info & SWD_SEQUENCE_DIN) {
        while (n) {
            val = 0U;
            for (k = 8U; k && n; k--, n--) {
                DEDIC_SW_READ_BIT(bit);
                val >>= 1;
                val |= bit << 7;
            }
            val >>= k;
            *swdi++ = (uint8_t)val;
        }
    } else {
        while (n) {
            val = *swdo++;
            for (k = 8U; k && n; k--, n--) {
                DEDIC_SW_WRITE_BIT(val);
                val >>= 1;
            }
        }
    }
}
#endif

#if (DAP_SWD != 0)

// SWD Transfer I/O
//   request: A[3:2] RnW APnDP
//   data:    DATA[31:0]
//   return:  ACK[2:0]
#define SWD_TransferFunction(speed) /**/                                                \
    static uint8_t SWD_Transfer##speed(uint32_t request, uint32_t *data)                \
    {                                                                                   \
        uint32_t ack;                                                                   \
        uint32_t bit;                                                                   \
        uint32_t val;                                                                   \
        uint32_t parity;                                                                \
                                                                                        \
        uint32_t n;                                                                     \
                                                                                        \
        /* Packet Request */                                                            \
        parity = 0U;                                                                    \
        DEDIC_SW_WRITE_BIT(1U); /* Start Bit */                                         \
        bit = request >> 0;                                                             \
        DEDIC_SW_WRITE_BIT(bit); /* APnDP Bit */                                        \
        parity += bit;                                                                  \
        bit = request >> 1;                                                             \
        DEDIC_SW_WRITE_BIT(bit); /* RnW Bit */                                          \
        parity += bit;                                                                  \
        bit = request >> 2;                                                             \
        DEDIC_SW_WRITE_BIT(bit); /* A2 Bit */                                           \
        parity += bit;                                                                  \
        bit = request >> 3;                                                             \
        DEDIC_SW_WRITE_BIT(bit); /* A3 Bit */                                           \
        parity += bit;                                                                  \
        DEDIC_SW_WRITE_BIT(parity); /* Parity Bit */                                    \
        DEDIC_SW_WRITE_BIT(0U);     /* Stop Bit */                                      \
        DEDIC_SW_WRITE_BIT(1U);     /* Park Bit */                                      \
                                                                                        \
        /* Turnaround */                                                                \
        DEDIC_PIN_SWDIO_OUT_DISABLE();                                                  \
        for (n = DAP_Data.swd_conf.turnaround; n; n--) {                                \
            DEDIC_SW_CLOCK_CYCLE();                                                     \
        }                                                                               \
                                                                                        \
        /* Acknowledge response */                                                      \
        DEDIC_SW_READ_BIT(bit);                                                         \
        ack = bit << 0;                                                                 \
        DEDIC_SW_READ_BIT(bit);                                                         \
        ack |= bit << 1;                                                                \
        DEDIC_SW_READ_BIT(bit);                                                         \
        ack |= bit << 2;                                                                \
                                                                                        \
        if (ack == DAP_TRANSFER_OK) { /* OK response */                                 \
            /* Data transfer */                                                         \
            if (request & DAP_TRANSFER_RnW) {                                           \
                /* Read data */                                                         \
                val = 0U;                                                               \
                parity = 0U;                                                            \
                for (n = 32U; n; n--) {                                                 \
                    DEDIC_SW_READ_BIT(bit); /* Read RDATA[0:31] */                      \
                    parity += bit;                                                      \
                    val >>= 1;                                                          \
                    val |= bit << 31;                                                   \
                }                                                                       \
                DEDIC_SW_READ_BIT(bit); /* Read Parity */                               \
                if ((parity ^ bit) & 1U) {                                              \
                    ack = DAP_TRANSFER_ERROR;                                           \
                }                                                                       \
                if (data) {                                                             \
                    *data = val;                                                        \
                }                                                                       \
                /* Turnaround */                                                        \
                for (n = DAP_Data.swd_conf.turnaround; n; n--) {                        \
                    DEDIC_SW_CLOCK_CYCLE();                                             \
                }                                                                       \
                DEDIC_PIN_SWDIO_OUT_ENABLE();                                           \
            } else {                                                                    \
                /* Turnaround */                                                        \
                for (n = DAP_Data.swd_conf.turnaround; n; n--) {                        \
                    DEDIC_SW_CLOCK_CYCLE();                                             \
                }                                                                       \
                DEDIC_PIN_SWDIO_OUT_ENABLE();                                           \
                /* Write data */                                                        \
                val = *data;                                                            \
                parity = 0U;                                                            \
                for (n = 32U; n; n--) {                                                 \
                    DEDIC_SW_WRITE_BIT(val); /* Write WDATA[0:31] */                    \
                    parity += val;                                                      \
                    val >>= 1;                                                          \
                }                                                                       \
                DEDIC_SW_WRITE_BIT(parity); /* Write Parity Bit */                      \
            }                                                                           \
            /* Capture Timestamp */                                                     \
            if (request & DAP_TRANSFER_TIMESTAMP) {                                     \
                DAP_Data.timestamp = TIMESTAMP_GET();                                   \
            }                                                                           \
            /* Idle cycles */                                                           \
            n = DAP_Data.transfer.idle_cycles;                                          \
            if (n) {                                                                    \
                DEDIC_PIN_SWDIO_OUT(0U);                                                \
                for (; n; n--) {                                                        \
                    DEDIC_SW_CLOCK_CYCLE();                                             \
                }                                                                       \
            }                                                                           \
            DEDIC_PIN_SWDIO_OUT(1U);                                                    \
            return ((uint8_t)ack);                                                      \
        }                                                                               \
                                                                                        \
        if ((ack == DAP_TRANSFER_WAIT) || (ack == DAP_TRANSFER_FAULT)) {                \
            /* WAIT or FAULT response */                                                \
            if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) != 0U)) { \
                for (n = 32U + 1U; n; n--) {                                            \
                    DEDIC_SW_CLOCK_CYCLE(); /* Dummy Read RDATA[0:31] + Parity */       \
                }                                                                       \
            }                                                                           \
            /* Turnaround */                                                            \
            for (n = DAP_Data.swd_conf.turnaround; n; n--) {                            \
                DEDIC_SW_CLOCK_CYCLE();                                                 \
            }                                                                           \
            DEDIC_PIN_SWDIO_OUT_ENABLE();                                               \
            if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) == 0U)) { \
                DEDIC_PIN_SWDIO_OUT(0U);                                                \
                for (n = 32U + 1U; n; n--) {                                            \
                    DEDIC_SW_CLOCK_CYCLE(); /* Dummy Write WDATA[0:31] + Parity */      \
                }                                                                       \
            }                                                                           \
            DEDIC_PIN_SWDIO_OUT(1U);                                                    \
            return ((uint8_t)ack);                                                      \
        }                                                                               \
                                                                                        \
        /* Protocol error */                                                            \
        for (n = DAP_Data.swd_conf.turnaround + 32U + 1U; n; n--) {                     \
            DEDIC_SW_CLOCK_CYCLE(); /* Back off data phase */                           \
        }                                                                               \
        DEDIC_PIN_SWDIO_OUT_ENABLE();                                                   \
        DEDIC_PIN_SWDIO_OUT(1U);                                                        \
        return ((uint8_t)ack);                                                          \
    }

#undef PIN_DELAY
#define PIN_DELAY() PIN_DELAY_FAST()
SWD_TransferFunction(Fast)

#undef PIN_DELAY
#define PIN_DELAY() PIN_DELAY_SLOW(DAP_Data.clock_delay)
    SWD_TransferFunction(Slow)

    // SWD Transfer I/O
    //   request: A[3:2] RnW APnDP
    //   data:    DATA[31:0]
    //   return:  ACK[2:0]
    uint8_t Dedic_IO_SWD_Transfer(uint32_t request, uint32_t *data)
{
    if (DAP_Data.fast_clock) {
        return SWD_TransferFast(request, data);
    } else {
        return SWD_TransferSlow(request, data);
    }
}

#endif /* (DAP_SWD != 0) */
